package model.editormodel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Observable;

import locsi.UserSettings;
import model.LogicType;
import view.UtilityConstants;

/**
 * <b>Erbt:</b> <code>Observable</code> <br>
 * <b>Zugehoerige Observer:</b> <code>EditorArea</code> <br>
 * <b>In Verbindung stehende Klassen:</b>
 * <code>Check, CircuitModel, LocsiParser, ComponentHandler</code><br>
 * <b>Funktion:</b> Verwaltet die aktuell vorhandene Schaltung. <br>
 * <br>
 * Enthaelt Methoden zum Hinzufuegen, Loeschen, Verschieben, Umbennen und
 * Verbinden von Komponenten, zum Exportieren bzw. Speichern als Bauteil bzw.
 * Bild und zum Abrufen von Schaltungsinformationen. Bildet die zentrale Einheit
 * des <code>editormodel</code>-Package. Diese Klasse wird von
 * <code>EditorArea</code> ueberwacht.
 * 
 * @author Team Locsi
 * 
 */
public class EditorModel extends Observable {

	/**
	 * Mit diesem Enum besteht die Möglichkeit genauere Informationen beim
	 * speichern bzw. exportieren einer Schaltung an die aufrufende Klasse
	 * zurueck zu geben
	 * 
	 * @author Team Locsi
	 * 
	 */
	public static enum SaveCircuitResponse {
		NAME_ALREADY_DEFINED, SOME_CHECK_FAILED, SAVE_OK
	}

	private Check checker;
	private CircuitModel cModel;
	private LocsiParser parser;
	private ComponentHandler compHandler;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>EditorModel</code>-Objekt.
	 * Dabei werden auch ein <code>LocsiParser</code>-, ein
	 * <code>ComponentHandler</code>, ein <code>Check</code>-Objekt und ein
	 * <code>CircuitModel</code> erzeugt.
	 */
	public EditorModel() {
		this.parser = new LocsiParser(this);
		this.compHandler = new ComponentHandler();
		this.checker = new Check(this);
		this.cModel = new CircuitModel();
		this.cModel.setName("main");
	}

	/**
	 * Ermoeglicht den Zugriff auf das <code>Check</code>-Objekt.
	 * 
	 * @return <code>Check</code> - das <code>Check</code>-Objekt
	 */
	public Check getChecker() {
		return checker;
	}

	/**
	 * Diese Methode gibt das Model der aktuellen Schaltung zurueck
	 * 
	 * @return <code>CircuitModel</code> - das aktuelle
	 *         <code>CircuitModel</code>-Objekt
	 */
	public CircuitModel getCircuitModel() {
		return cModel;
	}

	/**
	 * Gibt den <code>LocsiParser</code> zurueck.
	 * 
	 * @return <code>LocsiParser</code> - den <code>LocsiParser</code>
	 */
	public LocsiParser getParser() {
		return parser;
	}

	/**
	 * Gibt das <code>ComponentHandler</code>-Objekt zurueck.
	 * 
	 * @return <code>ComponentHandler</code> - das <code>ComponentHandler</code>
	 *         -Objekt
	 */
	public ComponentHandler getComponentHandler() {
		return this.compHandler;
	}

	/**
	 * Diese Methode ermoeglicht das Hinzufuegen eines Bauteils zum
	 * <code>CircuitModel</code>.
	 * 
	 * @param type
	 *            <code>LogicType</code> - Logischer Typ des Elements
	 * @param name
	 *            <code>String</code> - Gewuenschter Name fuer das Element,
	 *            <code>null</code> falls der Name durch das System vergeben
	 *            werden soll
	 * @param xCoord
	 *            <code>double</code> - x-Koordinate des Bauteils in Pixel
	 * @param yCoord
	 *            <code>double</code> - y-Koordinate des Bauteils in Pixel
	 * @return <code>LogicEditorElement</code> - das eingefuegte LogicEditorElement
	 * 			falls einfuegen erfolgreich, null sonst
	 */
	public LogicEditorElement addLogicElement(LogicType type, String name, double xCoord,
			double yCoord) {
		List<LogicEditorElement> info = new ArrayList<LogicEditorElement>();

		// sicherstellen ob die uebergebenenen Informationen im geforderten
		// Wertebereich liegen
		if (type != null & xCoord >= 0 & yCoord >= 0) {
			cModel.increaseIdCounter();
			String distinctName = generateSystemName(name, type);
			LogicEditorElement elementToAdd = null;
			if (type == LogicType.SELFDEFINED) {
				CircuitModel cModel = parser
						.readSelfDefinedModel(UtilityConstants.SD_DIRECTORY
								+ name);

				// pruefen ob das Bauteil geladen werden konnte
				if (cModel != null) {
					cModel.setName(name);
					elementToAdd = new LogicSelfdefinedEditorElement(
							distinctName, xCoord, yCoord, cModel);
				} else {
					return null;
				}
			} else {
				elementToAdd = new LogicEditorElement(type, distinctName,
						xCoord, yCoord);

			}

			// Hinzufuegen des Elements mit eindeutigem Namen

			cModel.getElementsOnEditor().put(distinctName, elementToAdd);

			// Information fuer die Observer erzeugen
			info.add(elementToAdd);
			this.setChanged();
			this.notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.ADD_ELEMENT, Collections
							.unmodifiableList(info)));

			// wenn vorher leer war muss typ immer abgefragt werden und dann
			// abfrage ob ein flipflop, clock oder selfdefined eingefügt wurde
			if (cModel.getElementsOnEditor().size() == 1
					|| LogicType.FF_CL_SD.contains(type)) {
				this.testTypeOfCircuit();
			}

			return elementToAdd;

			// sind die Uebergabeparameter nicht im geforderten Wertebereich > 0
			// so kann das Element nicht hinzugefuegt werden
		} else {

			// Information fuer die Observer erzeugen
			this.setChanged();
			this.notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.ERROR_ADDELEMENT));
			return null;
		}
	}

	/**
	 * Diese Methode generiert mit den uebergebenen Informationen einen Namen,
	 * bisher noch nicht verwendet wurde. Die Eindeutigkeit wird mit Hilfe des
	 * IdCounters aus dem <code>CircuitModel</code> erzeugt, indem es immer um
	 * eins erhoeht wird. Es sei denn es liegt ein Duplikat vor, so wird dieser
	 * IdCounter solange erhoeht bis der Name eindeutig wird
	 * 
	 * @param name
	 *            <code>String</code> - vorgeschlagener Name des Bauteils oder
	 *            <code>null</code> falls der Name durchs System erzeugt werden
	 *            soll
	 * @param type
	 *            <code>LogicType</code> dieser ist fuer die Namensgenerierung
	 *            notwendig
	 * @return <code>String</code> eindeutiger Name
	 */
	private String generateSystemName(String name, LogicType type) {
		String distinctName = "";

		// Sollte kein Name vorgegeben worden sein, so wird dieser erzeugt
		if (name == null) {

			// Die erzeugten Namen folgen einem festen Schema "Type_Id"
			distinctName = type.name() + "_" + cModel.getIdCounter();
			boolean isNotDistinct = cModel.getElementsOnEditor().containsKey(
					distinctName);

			// Falls der Name in dieser Form schon in der Schaltung vorkommt
			// wird der IdCounter erhoeht und der neue Name erneut geprueft
			if (isNotDistinct) {
				while (isNotDistinct) {
					cModel.increaseIdCounter();
					distinctName = type.name() + "_" + cModel.getIdCounter();
					isNotDistinct = cModel.getElementsOnEditor().containsKey(
							distinctName);
				}
			}

			// wurde ein Name vorgegeben so wird seine Eindeutigkeit geprueft
		} else {
			distinctName = name + "_" + cModel.getIdCounter();
			boolean isNotDistinct = cModel.getElementsOnEditor().containsKey(
					distinctName);

			// ist dieser nicht eindeutig, so wird er angepasst
			if (isNotDistinct) {
				while (isNotDistinct) {
					cModel.increaseIdCounter();
					distinctName = name + "_" + cModel.getIdCounter();
					isNotDistinct = cModel.getElementsOnEditor().containsKey(
							distinctName);
				}
			}
		}
		return distinctName;
	}

	/**
	 * Diese Methode ermoeglicht das Loeschen von Bauteilen im
	 * <code>CircuitModel</code>.
	 * 
	 * @param elementsToDelete
	 *            <code>List</code><<code>String></code> - Liste mit den Namen
	 *            der zu loeschenden Bauteile
	 * @return <code>boolean</code> - <code>true</code> falls alle Elemente
	 *         geloescht wurden, ansonsten <code>false</code>
	 */
	public boolean deleteLogicElements(List<String> elementsToDelete) {
		boolean allRemoved = true;
		List<LogicEditorElement> info = new ArrayList<LogicEditorElement>();

		if (elementsToDelete != null) {

			// Die uebergebnenen Namen der zu loeschenden Elemente werden durch
			// laufen und vom Circuit Model entfernt
			for (String distinctName : elementsToDelete) {
				LogicEditorElement removed = cModel.getElementsOnEditor()
						.remove(distinctName);

				if (removed != null) {

					// Wird das Element entfernt, so muessen auch alle
					// zugehoerigen Verbindungen entfernt werden
					removeAllConnections(removed);
					info.add(removed);
				} else {

					// Kann ein Element nicht geloescht werden, weil dessen
					// Name nicht in der Schaltung also das Element selbst
					// nicht existiert, wird dies durch das Flag allRemoved
					// angezeigt
					allRemoved = false;
				}
			}
		} else {
			allRemoved = false;
		}
		setChanged();
		
		// Es werden alle Elemente mituebergeben, die geloescht wurden
		notifyObservers(new EditorMessage(
				EditorMessage.EditorMessageType.DELETE_ELEMENTS,
				Collections.unmodifiableList(info)));
		this.testTypeOfCircuit();
		return allRemoved;
	}

	/**
	 * Diese Methode entfernt alle Verbindungen, die mit dem uebergebenen
	 * Element in Verbindung stehen
	 * 
	 * @param toRemove
	 *            <code>LogicEditorElement</code> - Element dessen Verbindungen
	 *            geloescht werden sollen
	 */
	private void removeAllConnections(LogicEditorElement toRemove) {

		// zuerst werden alle Quellen bestimmt und deren Ziele auf null gesetzt,
		// da diese auf dem zu loeschenden Element liegen
		for (Input inp : toRemove.getInputs().values()) {
			Output sourceOut = inp.getSourceOutput();
			if (sourceOut != null) {
				sourceOut.removeTargetInput(inp);
			}
		}

		// danache werden alle Ziele bestimmt und deren Quellen auf null
		// gesetzt, da diese auf dem zu loeschendne Element liegen
		for (Output out : toRemove.getOutputs().values()) {
			for (Input inp : out.getTargetInputs()) {
				if (inp != null) {
					inp.setSourceOutput(null);
				}
			}
		}
	}

	/**
	 * Diese Methode ermoeglicht die Aenderung des Namens eines Bauteils aus dem
	 * <code>CircuitModel</code>.
	 * 
	 * @param oldName
	 *            <code>String</code> - alter Name des Bauteils
	 * @param newName
	 *            <code>String</code> - neuer Name des Bauteils
	 * @return <code>boolean</code> - falls Aenderung erfolgreich
	 *         <code>true</code>, ansonsten <code>false</code>
	 */
	public boolean renameElement(String oldName, String newName) {

		// Es wird sichergestellt, dass der uebergebene Name oldName auch
		// in der Schaltung vorhanden ist
		if (oldName != null & newName != null
				& cModel.getElementsOnEditor().containsKey(oldName)) {
			LogicEditorElement toRefactor = cModel.getElementsOnEditor().get(
					oldName);

			// wenn der alte name gleich dem neuen ist, muss nichts geaendert
			// werden
			if (oldName.equals(newName)) {
				return true;
			}

			// Ausserdem muss sichergestellt sein, dass der neue Name nicht
			// bereits schon vergeben wurde
			if (!cModel.getElementsOnEditor().containsKey(newName)) {
				toRefactor.setName(newName);
				cModel.getElementsOnEditor().put(newName, toRefactor);
				cModel.getElementsOnEditor().remove(oldName);

				String[] oNnN = new String[2];
				oNnN[0] = oldName;
				oNnN[1] = newName;

				// Die Observer werden ueber die erfolgreiche Aenderung
				// benachrichtigt
				this.setChanged();
				this.notifyObservers(new EditorMessage(
						EditorMessage.EditorMessageType.REFACTORED_ELEMENT,
						oNnN));
				return true;
			} else {

				// ist der neue Name bereits vergeben wird dies den Observern
				// mitgeteilt
				this.setChanged();
				this.notifyObservers(new EditorMessage(
						EditorMessage.EditorMessageType.ERROR_REFACTORING));
				return false;
			}
		}
		this.setChanged();

		// sollte keine Aenderung moeglich gewesen sein und dies nicht durch
		// den neuen Namen verschuldet wurde, bekommt die View eine Nachricht
		// mit dem Enum REFACTORED_ELEMENT und null
		this.notifyObservers(new EditorMessage(
				EditorMessage.EditorMessageType.ERROR_REFACTORING));
		return false;

	}

	/**
	 * Diese Methode aendert die Koordinaten der mituebergebenen Bauteile im
	 * <code>CircuitModel</code>.
	 * 
	 * @param elementsToMove
	 *            <code>List</code><<code>String></code> - Array mit den Namen
	 *            der zu aendernden Bauteile
	 * @param xDiff
	 *            <code>double</code> - Wert in Pixel, um den die x-Koordinate
	 *            veraendert wird
	 * @param yDiff
	 *            <code>double</code> - Wert in Pixel, um den die y-Koordinate
	 *            veraendert wird
	 * @return <code>boolean</code> - <code>true</code> falls alle angegebenen
	 *         Element verschoben werden konnten, ansonsten <code>false</code>
	 */
	public boolean moveElements(List<String> elementsToMove, double xDiff,
			double yDiff) {
		boolean allMoved = true;

		// dieser Wert sorgt dafuer, dass beim hinausschieben ueber die
		// Schaltung, also wenn negative Koordinaten entstehen ein Puffer
		// von UPPERLEFTMARGIN erzeugt wird
		final double UPPERLEFTMARGIN = 20;

		double minX = 0;
		double minY = 0;
		boolean movedAll = false;

		// verschieben aller uebergebenen Elemente
		LogicEditorElement current;
		double currentX;
		double currentY;
		for (String distinctName : elementsToMove) {
			if (distinctName != null
					&& cModel.getElementsOnEditor().containsKey(distinctName)) {
				current = cModel.getElementsOnEditor().get(distinctName);
				currentX = current.getXCoord();
				currentY = current.getYCoord();

				// das Element wird verschoben
				current.setXCoord(currentX + xDiff);
				current.setYCoord(currentY + yDiff);

				// der kleinste xWert wird festgehalten
				if (minX > current.getXCoord()) {
					minX = current.getXCoord();
				}

				// der kleinste yWert wird festgehalten
				if (minY > current.getYCoord()) {
					minY = current.getYCoord();
				}
			} else {
				allMoved = false;
			}
		}

		// nun muss noch geprueft werden ob negative Koordinaten entstanden sind
		// und wenn ja, dann muessen dementsprechend alle Elemente korrigiert
		// werden
		if (minX < 0 & minY < 0) {
			moveAllElements(minX - UPPERLEFTMARGIN, minY - UPPERLEFTMARGIN);
			movedAll = true;
		} else {
			if (minX < 0) {
				moveAllElements(minX - UPPERLEFTMARGIN, 0);
				movedAll = true;
			}
			if (minY < 0) {
				moveAllElements(0, minY - UPPERLEFTMARGIN);
				movedAll = true;
			}
		}
		List<LogicEditorElement> movedElements = new ArrayList<LogicEditorElement>();

		// Abhaengig davon, ob alle Elemente verschoben werden mussten, oder
		// nur die Uebergebenen wird entsprechend die Liste fuer die
		// EditorMessage generiert
		if (movedAll) {
			for (LogicEditorElement e : cModel.getElementsOnEditor().values()) {
				movedElements.add(e);
			}
		} else {
			for (String key : elementsToMove) {
				movedElements.add(cModel.getElementsOnEditor().get(key));
			}
		}
		setChanged();
		notifyObservers(new EditorMessage(
				EditorMessage.EditorMessageType.MOVE_ELEMENTS,
				Collections.unmodifiableList(movedElements)));
		return allMoved;
	}

	/**
	 * Diese Methode verschiebt alle vorhandenen Elemente auf der Schaltung um
	 * die jeweiligen Werte von xDiff und yDiff
	 * 
	 * @param xDiff
	 *            <code>double</code> - horizontaler Wert um den verschoben wird
	 * @param yDiff
	 *            <code>double</code> - vertikaler Wert um den verschoben wird
	 */
	private void moveAllElements(double xDiff, double yDiff) {
		double currentX;
		double currentY;

		// verschieben aller Elemente um -xDiff und -yDiff
		for (LogicEditorElement current : cModel.getElementsOnEditor().values()) {
			currentX = current.getXCoord();
			currentY = current.getYCoord();
			current.setXCoord(currentX - xDiff);
			current.setYCoord(currentY - yDiff);
		}
	}

	/**
	 * Diese Methode ermoeglicht das Verbinden zweier Bauteile.
	 * 
	 * @param sourcePort
	 *            <code>Output</code> - Ausgang des Quellelements
	 * @param targetPort
	 *            <code>Input</code> - Eingang des Zielelements
	 * @return <code>boolean</code> - <code>true</code> falls die Verbindung
	 *         gesetzt wurde, ansonsten <code>false</code>
	 */
	public boolean setConnection(Output sourcePort, Input targetPort) {
		if (sourcePort != null & targetPort != null) {

			// pruefen ob der Eingang nicht bereits schon belegt ist
			if (targetPort.getSourceOutput() == null) {
				targetPort.setSourceOutput(sourcePort);
			} else {
				return false;
			}

			// keine Pruefung notwendig, da ein Ausgang mit mehreren Eingaengen
			// verbunden sein kann
			sourcePort.setTargetInput(targetPort);
			setChanged();

			// Den Observern wird mitgeteilt, welcher Output und Input an der
			// Verbindung beteiligt ist
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.SET_CONNECTION, targetPort,
					sourcePort));
			this.testTypeOfCircuit();
			return true;
		}
		return false;
	}

	/**
	 * Diese Methode entfernt die Verbindung zwischen einem Eingang und einem
	 * Ausgang
	 * 
	 * @param targetPort
	 *            <code>Input</code> - Input der an der Verbindung beteiligt ist
	 * @param sourcePort
	 *            <code>Output</code> - Output der an der Verbindung beteiligt
	 *            ist
	 * @return <code>boolean</code> - <code>true</code> falls die Verbindung
	 *         entfernt wurde, <code>false</code> falls nicht
	 */
	public boolean deleteConnection(Input targetPort, Output sourcePort) {
		if (targetPort != null & sourcePort != null) {

			// die jeweiligen Referenzen auf den Nachfolger bzw Vorgaenger
			// werden geloescht und es wird geprueft ob die Verbindung
			// ueberhaupt vorhanden war
			if (targetPort.getSourceOutput() == sourcePort) {
				targetPort.setSourceOutput(null);
			} else {
				return false;
			}
			if (sourcePort.getTargetInputs().contains(targetPort)) {
				sourcePort.removeTargetInput(targetPort);
			} else {
				return false;
			}
			setChanged();

			// zugehoerige Observer werden benachrichtigt
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.DELETE_CONNECTION,
					targetPort, sourcePort));
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Diese Methode entfernt die Verbindungen zwischen den ubergebenen Ein- und
	 * Ausgaengen
	 * 
	 * @param toDelete
	 *            <code>Map</code><<code>Input, Output></code> - Paare von Ein-
	 *            und Ausgaengen
	 * @return <code>boolean</code> - konnte eine der Verbindungen nicht
	 *         entfernt werden so wird <code>false</code> zurueckgegeben
	 *         ansonsten <code>true</code>
	 */
	public boolean deleteConnections(Map<Input, Output> toDelete) {
		boolean notAllChanged = true;
		if (toDelete != null) {

			// durchlaufen aller Verbindungen
			for (Map.Entry<Input, Output> s : toDelete.entrySet()) {
				if (s.getKey() != null & s.getValue() != null) {

					// die jeweiligen Referenzen auf den Nachfolger bzw
					// Vorgaenger
					// werden geloescht und es wird geprueft ob die Verbindung
					// ueberhaupt vorhanden war
					if (s.getKey().getSourceOutput() == s.getValue()) {
						s.getKey().setSourceOutput(null);
					} else {
						notAllChanged = false;
					}
					if (s.getValue().getTargetInputs().contains(s.getKey())) {
						s.getValue().removeTargetInput(s.getKey());
					} else {
						notAllChanged = false;
					}
				} else {
					notAllChanged = false;
				}
			}
			setChanged();

			// zugehoerige Observer werden benachrichtigt
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.DELETE_CONNECTIONS,
					toDelete));
			return notAllChanged;
		} else {
			return false;
		}
	}

	/**
	 * Dient zum Speichern einer Schaltung als Bauteil. Dabei werden erst die
	 * Checks durchgefuehrt und danach der <code>LocsiParser</code> angestossen,
	 * die Speicherung durchzufuehren.
	 * 
	 * @return <code>EditorModel.SaveCircuitResponse</code> - SAVE_OK,
	 *         NAME_ALREADY_DEFINED oder SOME_CHECK_FAILED
	 */
	public EditorModel.SaveCircuitResponse exportComponent(String name) {
		if (cModel.isComponent()) {
			// nachdem die Checks erfolgreich waren, wird geprueft ob das
			// Bauteil auch tatsaechlich gespeichert wurde
			cModel.setName(name);
			if (this.parser.createSelfdefinedModel(cModel)) {
				return EditorModel.SaveCircuitResponse.SAVE_OK;
			} else {

				// Bauteil konnte nicht ohne Probleme gespeichert werden
				return EditorModel.SaveCircuitResponse.NAME_ALREADY_DEFINED;
			}
		} else {

			// die Schaltung ist kein Bauteil
			return EditorModel.SaveCircuitResponse.SOME_CHECK_FAILED;
		}
	}

	/**
	 * Dient zum Importieren eines selbstdefinierten Bauteils. Dabei wird der
	 * <code>LocsiParser</code> angestossen, dies umzusetzen.
	 * 
	 * @param path
	 *            <code>String</code> - Pfad auf dem sich das einzulesende
	 *            Bauteil befindet
	 * 
	 * @return <code>boolean</code> - es wird <code>true</code> zurueck gegeben
	 *         falls das Bauteil importiert werden konnten, ansonsten
	 *         <code>false</code>
	 */
	public boolean importComponent(String path) {
		CircuitModel component = this.parser.readSelfDefinedModel(path);

		// Pruefen ob das Bauteil gelesen werden konnte
		if (component != null) {

			// das Bauteil als Schaltung im EditorModel setzen
			this.cModel = component;
			setChanged();

			// benachtrichtigen der Observer
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.IMPORT_COMPONENT,
					Collections.unmodifiableList(this.cModel
							.getElementsListOnEditor())));
			this.testTypeOfCircuit();
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Dient zum Laden einer Schaltung. Dabei wird der <code>LocsiParser</code>
	 * angestossen, dies umzusetzen.
	 * 
	 * @param path
	 *            <code>String</code> - Pfad der zu ladenden Schaltung
	 * @return <code>boolean</code> - <code>true</code> wenn Schaltung geladen
	 *         werden konnte, <code>false</code> sonst
	 */
	public boolean loadCircuit(String path) {
		CircuitModel cModel = this.parser.readCircuit(path);
		if (cModel != null) {

			// setzen der neuen Schaltung auf dem EditorModel
			this.cModel = cModel;

			// Konnte die Schaltung geladen werden, werden die Observer
			// benachrichtigt
			this.setChanged();
			this.notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.LOAD_CIRCUIT, Collections
							.unmodifiableList(this.cModel
									.getElementsListOnEditor())));
			this.testTypeOfCircuit();
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Diese Methode dient zum Speichern einer Schaltung. Hierbei wird der
	 * <code>LocsiParser</code> angestossen, dies umzusetzen.
	 * 
	 * @param path
	 *            <code>String</code> - Pfad an dem die Schaltung gespeichert
	 *            werden soll.
	 * @param fileName
	 *            <code>String</code> - Name der Schaltung
	 * @return <code>boolan</code> - <code>true</code> falls das Bauteil
	 *         gespeichert wurde, ansonsten <code>false</code>
	 * 
	 */
	public boolean saveCircuit(String fileName, String path) {
		boolean isSaved = this.parser.saveCircuit(fileName, path, cModel);
		if (isSaved) {
			setChanged();
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.SAVE_CIRCUIT));
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Dient zur Erstellung einer neuen Schaltung. Dabei wird das
	 * <code>EditorModel</code> zurueckgesetzt.
	 * 
	 * @return <code>boolean</code> - <code>true</code> falls eine neue
	 *         Schaltung erzeugt werden konnte, ansonsten <code>false</code>
	 */
	public boolean newCircuit() {
		cModel = new CircuitModel();
		setChanged();

		// benachrichtigen des Observers
		notifyObservers(new EditorMessage(
				EditorMessage.EditorMessageType.NEW_CIRCUIT,
				Collections.unmodifiableList(this.cModel
						.getElementsListOnEditor())));
		this.testTypeOfCircuit();
		return true;
	}

	/**
	 * Diese Methode prueft ob die aktuelle Schaltung leer ist oder nicht.
	 * 
	 * @return <code>boolean</code> - besitzt die Schaltung kein Element so wird
	 *         <code>true</code> zureuck gegeben, ansonsten <code>false</code>
	 */
	public boolean isEmptyCircuit() {
		return this.cModel.getElementsOnEditor().isEmpty();
	}

	/**
	 * Uebergibt dem zugehoerigen <code>Observer</code> Informationen ueber die
	 * fehlgeschlagenen Checks
	 * 
	 * @param checkEM
	 *            <code>ErrorMessage</code> - ein Mitteilungsobjekt welche eine
	 *            genaue Untergliederung der fehlgeschlagenen Checks beinhaltet
	 */
	protected void sendIncorrectElements(ErrorMessage checkEM) {
		this.setChanged();
		this.notifyObservers(new EditorMessage(
				EditorMessage.EditorMessageType.CHECK_MESSAGE, checkEM));
	}

	/**
	 * Diese Methode prueft beim aktuellen <code>CircuitModel</code>, ob die
	 * Schaltung ein Schaltwerk oder ein Schaltnetz ist und gibt die
	 * entsprechende Nachricht an die Observer des <code>EditorModel</code>
	 * weiter.
	 */
	private void testTypeOfCircuit() {
		for (int i = 0; i < 100; i++) {
			this.setChanged();

			// ist kein Element auf der Schaltung vorhanden so wird dies dem
			// Observerver mitgeteilt
			if (isEmptyCircuit()) {
				this.notifyObservers(new EditorMessage(
						EditorMessage.EditorMessageType.IS_DEFAULT));

				// ansonsten wird abhaengig vom Schaltungstyp die entsprechende
				// EditorMessage an den Observer geschickt
			} else {
				if (cModel.isLogicCircuit()) {
					this.notifyObservers(new EditorMessage(
							EditorMessage.EditorMessageType.IS_LOGICCIRCUIT));
				} else {
					// es muss unterschieden werden ob es sich wirklich um ein
					// Schaltnetz handelt oder ob die Anzahl der Element zu groß
					// war um den Schaltungstyp zu bestimmen
					if (this.getCircuitModel().getElementsOnEditor().size() < UserSettings.maxElemCircuitInfo) {
						this.notifyObservers(new EditorMessage(
								EditorMessage.EditorMessageType.IS_LOGICNET));
					} else {
						this.notifyObservers(new EditorMessage(
								EditorMessage.EditorMessageType.IS_UNKNOWN));
					}
				}
			}
		}
	}

	/**
	 * Methode, die die Bauteile am Raster ausrichtet. Falls isPossible falsch
	 * ist, ist Ausrichten nicht moeglich und es werden die alten Koordinaten an
	 * die Observer geschickt
	 * 
	 * @param isPossible
	 *            gibt an, ob Ausrichten moeglich ist
	 */
	public void positioningToRaster(boolean isPossible) {
		int x = 0;
		int y = 0;

		if (!isPossible) {
			setChanged();
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.RASTER_POSITIONING,
					cModel.getElementsListOnEditor()));
		} else {
			for (LogicEditorElement elem : cModel.getElementsOnEditor()
					.values()) {

				x = (int) elem.getXCoord();
				y = (int) elem.getYCoord();

				if (x % UtilityConstants.RASTER_SIZE >= UtilityConstants.RASTER_SIZE / 2) {
					elem.setXCoord(x
							+ (UtilityConstants.RASTER_SIZE - (x % UtilityConstants.RASTER_SIZE)));
				} else {
					elem.setXCoord(x - (x % UtilityConstants.RASTER_SIZE));
				}

				if (y % UtilityConstants.RASTER_SIZE >= UtilityConstants.RASTER_SIZE / 2) {
					elem.setYCoord(y
							+ (UtilityConstants.RASTER_SIZE - (y % UtilityConstants.RASTER_SIZE)));
				} else {
					elem.setYCoord(y - (y % UtilityConstants.RASTER_SIZE));
				}
			}

			setChanged();
			notifyObservers(new EditorMessage(
					EditorMessage.EditorMessageType.RASTER_POSITIONING,
					cModel.getElementsListOnEditor()));
		}
	}
	
	/**
	 * Diese Methode gibt eine nicht veraenderbare Liste der unverbundenen Eingaenge
	 * zurueck. Das wird gebraucht um die Funktion 'Schalter automatisch einfuegen'
	 * umzusetzen
	 * 
	 * @return Liste Liste mit den Inputs die unverbunden sind
	 */
	public List<Input> getUnrelatedInputs() {
		return cModel.getUnrelatedInputs();
	}
	
	/**
	 * Diese Methode gibt eine nicht veraenderbare Liste der unverbundenen Ausgaenge
	 * zurueck. Das wird gebraucht um die Funktion 'Lampen automatisch einfuegen'
	 * umzusetzen
	 * 
	 * @return Liste Liste mit den Outputs die komplett unverbunden sind
	 */
	public List<Output> getUnrelatedOutputs() {
		return cModel.getUnrelatedOutputs();
	}
}